﻿using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Aquarinum.Base.Infrastructure.Ioc;
using Aquarinum.Base.Infrastructure.WebSocket.Interface;
using Aquarinum.Base.Infrastructure.WebSocket.MessageHandler;
using Aquarinum.Base.Infrastructure.WebSocket.Utils;
using Aquarinum.Util.Interface.Log;
using Aquarinum.WebSocekt.Core.Dtos;
using Castle.Core.Internal;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;

namespace Aquarinum.Base.Infrastructure.WebSocket.InterfaceImp
{
    /// <summary>
    ///     websocket 消息处理
    /// </summary>
    public class WebSocketRequestTransfer : IWebSocketRequestTransfer
    {
        private readonly IMessageCacheHandler _messageCacheHandler;
        private readonly WebSocketConnectionContainer _webSocketConnectionContainer;

        /// <summary>
        ///     构造
        /// </summary>
        //public WebSocketRequestTransfer(WebSocketConnectionContainer webSocketConnectionContainer,
        //    IMessageCacheHandler messageCacheHandler)
        //{
        //    _webSocketConnectionContainer = webSocketConnectionContainer;
        //    _messageCacheHandler = messageCacheHandler;
        //}
        public WebSocketRequestTransfer(WebSocketConnectionContainer webSocketConnectionContainer)
        {
            _webSocketConnectionContainer = webSocketConnectionContainer;
        }

        #region 数据接受

        /// <inheritdoc />
        public async Task ReceiveAsync(System.Net.WebSockets.WebSocket socket,
            string messageJson, HttpContext context)
        {
            var messageBase = JsonConvert.DeserializeObject<MessageBase>(messageJson);
            if (messageBase.Type == ConstSocketMessageType.CLINET_HEARTBEAT) //客户端心跳信息
            {
                await SendMessageAsync(socket, new WebsocketMessageDto
                {
                    Type = ConstSocketMessageType.HEARTBEAT_SUCCESS,
                    Body = string.Empty,
                    From = "server",
                    Domain = context.Connection.LocalIpAddress.ToString(),
                    Id = Guid.NewGuid().ToString("N"),
                    To = _webSocketConnectionContainer.GetId(socket) ?? string.Empty,
                    Timestamp = DateTimeHelper.GetUnixTimestamp()
                }).ConfigureAwait(false);
            }
            else
            {
                var messageDto = JsonConvert.DeserializeObject<WebsocketMessageDto>(messageJson);
                if (messageDto != null)
                {
                    var handlers = MessageHandleStrategyFactory.GetInstance().GetStrategyInstance(messageDto.Type);
                    try
                    {
                        foreach (var handler in handlers) handler.HandleMessage(messageDto);
                    }
                    catch (Exception ex)
                    {
                        AquainumContainer.Resolve<IAquarinumLogger>().Error($"websocket消息：{messageJson}出错", ex);
                    }
                }
            }
        }

        #endregion


        #region 连接处理

        /// <inheritdoc />
        public async Task OnConnected(System.Net.WebSockets.WebSocket socket, HandShakeDto handShakeMessage,
            HttpContext context)
        {
            if (handShakeMessage == null)
            {
                await SendMessageAsync(socket, new WebsocketMessageDto
                {
                    Type = ConstSocketMessageType.CLINET_UNAUTH,
                    Body = string.Empty,
                    From = "server",
                    Domain = context.Connection.LocalIpAddress.ToString(),
                    Id = Guid.NewGuid().ToString("N"),
                    To = context.Connection.RemoteIpAddress.ToString(),
                    Timestamp = DateTimeHelper.GetUnixTimestamp()
                }).ConfigureAwait(false);
                socket.Dispose();
            }
            else
            {
                _webSocketConnectionContainer.AddSocket(
                    $"{handShakeMessage.User};{handShakeMessage.Resource}".TrimEnd(';'), socket);
                AquainumContainer.Resolve<IAquarinumLogger>().Info($"客户端{handShakeMessage.User}连接成功");
                await SendMessageAsync(socket, new WebsocketMessageDto
                {
                    Type = ConstSocketMessageType.CLINET_CONNECTED,
                    Body = string.Empty,
                    From = "server",
                    Domain = context.Connection.LocalIpAddress.ToString(),
                    Id = Guid.NewGuid().ToString("N"),
                    To = $"{handShakeMessage.User};{handShakeMessage.Resource}".TrimEnd(';'),
                    Timestamp = DateTimeHelper.GetUnixTimestamp()
                }).ConfigureAwait(false);
                // todo 补发所有需要补发的消息

                //var messages =
                //    _messageCacheHandler.GetMessageByToClient($"{handShakeMessage.User};{handShakeMessage.Resource}".TrimEnd(';'));
                //if (messages != null && messages.Any())
                //    await SendMessageAsync(socket, new WebsocketMessageDto
                //    {
                //        Type = ConstSocketMessageType.MESSAGE_RESEND,
                //        Body = JsonConvert.SerializeObject(messages),
                //        From = "server",
                //        Domain = context.Connection.LocalIpAddress.ToString(),
                //        Id = Guid.NewGuid().ToString("N"),
                //        To = $"{handShakeMessage.User};{handShakeMessage.Resource}".TrimEnd(';'),
                //        Timestamp = DateTimeHelper.GetUnixTimestamp()
                //    }).ConfigureAwait(false);
            }
        }


        /// <inheritdoc />
        public async Task OnDisconnected(System.Net.WebSockets.WebSocket socket)
        {
            var id = _webSocketConnectionContainer.GetId(socket);
            if (!id.IsNullOrEmpty())
                await _webSocketConnectionContainer.RemoveSocket(id)
                    .ConfigureAwait(false);
        }

        #endregion

        #region 数据发送

        public  Task SendMessageAsync(System.Net.WebSockets.WebSocket socket, WebsocketMessageDto socketMessage,
            long expireSecond = 0,
            bool needPersistence = false, bool needReceipt = false)
        {
            socketMessage.NeedReceipt = needReceipt;
            if (socket.State == WebSocketState.Open)
            {
                var serializedMessage = JsonConvert.SerializeObject(socketMessage);
                var encodedMessage = Encoding.UTF8.GetBytes(serializedMessage);
                return socket.SendAsync(new ArraySegment<byte>(encodedMessage,
                        0,
                        encodedMessage.Length),
                    WebSocketMessageType.Text,
                    true,
                    CancellationToken.None);
            }

            return Task.Run(() => { });
        }

        public async Task SendMessageAsync(string toClientId, WebsocketMessageDto socketMessage, long expireSecond = 0,
            bool needPersistence = false, bool needReceipt = false)
        {
            var socket = _webSocketConnectionContainer.GetSocketById(toClientId);
            if (socket != null)
                await SendMessageAsync(socket, socketMessage, expireSecond, needPersistence, needReceipt)
                    .ConfigureAwait(false);
        }

        public async Task PublishMessageToAllAsync(WebsocketMessageDto socketMessage, long expireSecond = 0,
            bool needPersistence = false,
            bool needReceipt = false)
        {
            var sockets = _webSocketConnectionContainer.GetAll().Values;

            foreach (var socket in sockets)
                await SendMessageAsync(socket, socketMessage, expireSecond, needPersistence, needReceipt)
                    .ConfigureAwait(false);
        }

        public async Task PublishMessageToGroupAsync(string groupId, WebsocketMessageDto socketMessage,
            long expireSecond = 0,
            bool needPersistence = false, bool needReceipt = false)
        {
            var sockets = _webSocketConnectionContainer.GetAllClientsFromGroup(groupId);

            foreach (var socket in sockets)
                await SendMessageAsync(socket.ClientId, socketMessage, expireSecond, needPersistence, needReceipt)
                    .ConfigureAwait(false);
        }

        #endregion
    }
}